package notifier

import (
	"am-dingtalk-webhook/model"
	"crypto/hmac"
	"crypto/sha256"
	"encoding/base64"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"net/url"
	"strings"
	"time"
)

type SimpleRequest struct {
	Text    SimpleRequestContent `json:"text"`
	At      SimpleRequestAt      `json:"at"`
	Msgtype string               `json:"msgtype"`
}

type SimpleRequestContent struct {
	Content string `json:"content"`
}

type SimpleRequestAt struct {
	AtMobiles []string `json:"atMobiles"`
	AtUserIds []string `json:"atUserIds"`
	IsAtAll   bool     `json:"isAtAll"`
}

//var UrlAddress = "https://oapi.dingtalk.com/robot/send?access_token=7b2ecf85d5a3a8a196f29487855f446355245d8a26814bb0db72f26bc71d9f22"
//var Secret = "SEC2d87a5c8098fada6c67a944602cfd6190c35c16da32543c99cf8a10514d83498"

// 查找一个字符串是否在[]string里
func stringInSlice(str string, strSli []string) (exist bool) {
	exist = false

	// 如果strSli为空， 则默认放行所有str
	if len(strSli) == 0 {
		exist = true
		return exist
	}

	for _, v := range strSli {
		if v == str {
			exist = true
		}
	}
	if !exist {
		fmt.Printf("%s 不存在与 %s 中\n", str, strSli)
	}
	return exist
}

// 根据prometheus的告警列表，生成告警信息列表
func convertDingAlertsToText(notification model.Notification, env string, includeSeverity []string) (content string, err error) {
	// annotations := notification.CommonAnnotations
	// robotURL = annotations["dingtalkRobot"]

	// 组装告警头部信息
	// 判断group的严重等级，是否需要发送 todo:只看这一个不准确
	groupSeverityExist := true
	if groupSeverity, ok := notification.CommonLabels["severity"]; ok {
		groupSeverityExist = stringInSlice(groupSeverity, includeSeverity)
	}
	if !groupSeverityExist {
		return "", nil
	}

	// 下面开始组装通知文本
	alertHeadText := fmt.Sprintf("【%s-监控告警】\n", env)
	// 添加通知状态 （告警/恢复）
	status := "无状态"
	if notification.Status == "firing" {
		status = "告警中"
	} else if notification.Status == "resolved" {
		status = "已恢复"
	} else {
		status = notification.Status
	}
	alertHeadText += fmt.Sprintf("告警状态：%s\n", status)
	if v, ok := notification.CommonLabels["severity"]; ok {
		alertHeadText += fmt.Sprintf("告警等级：%s\n", v)
	}
	alertHeadText += fmt.Sprintf("触发时间：%s\n", time.Now().Local().Format("2006-01-02 15:04:05"))

	// 添加公共标签
	if v, ok := notification.CommonLabels["alertname"]; ok {
		alertHeadText += fmt.Sprintf("告警类别：%s\n", v)
	}
	if v, ok := notification.CommonLabels["instance"]; ok {
		ip := strings.Split(v, ":")[0]
		alertHeadText += fmt.Sprintf("告警实例：%s\n", ip)
	}

	// 遍历alertManager推送的alert列表，解析后生成适合钉钉的文本
	alertLiText := ""
	for _, alert := range notification.Alerts {
		// 先判断alert的stringInSlice(groupSeverity, includeSeverity) 是否在允许列表中
		alertSeverityExist := true
		if alertSeverity, ok := alert.Labels["severity"]; ok {
			alertSeverityExist = stringInSlice(alertSeverity, includeSeverity)
		}
		if !alertSeverityExist {
			continue
		}

		alertLiText += "● "
		// 查看EndsAt，如果有，则已结束
		if alert.EndsAt.IsZero() {
			alertLiText += "[火]"
		} else {
			alertLiText += "[Done]"
		}
		// 按照每一条alert中annotation的标签优先级，依次提取 chinese > summary > description
		annotations := alert.Annotations
		if v, ok := annotations["chinese"]; ok {
			alertLiText += v
		} else if v, ok := annotations["summary"]; ok {
			alertLiText += v
		} else if v, ok := annotations["description"]; ok {
			alertLiText += v
		}

		// 附上时间
		// alertLiText += alert.StartsAt.Local().Format(" | 2006-01-02 15:04:05")

		alertLiText += "\n"

	}

	// 如果告警列表都没通过检测，全局返回为空
	if alertLiText == "" {
		return "", nil
	}
	return alertHeadText + alertLiText, nil
}

func SendWithSign(notification model.Notification, defaultRobot string, Secret string, env string, includeSeverity []string, atAll bool) {
	// 组装content
	content, _ := convertDingAlertsToText(notification, env, includeSeverity)
	fmt.Printf("要发给钉钉的告警文本：\n%s\n", content)

	if content == "" {
		fmt.Println("文本为空，不发送给钉钉")
		return
	}

	// 设置请求body
	requestBody := SimpleRequest{
		Text: SimpleRequestContent{
			Content: content,
		},
		At: SimpleRequestAt{
			AtMobiles: nil,
			AtUserIds: nil,
			IsAtAll:   atAll,
		},
		Msgtype: "text",
	}

	reqBodyBox, _ := json.Marshal(requestBody)
	body := string(reqBodyBox)

	//  构建 签名
	//  把timestamp+"\n"+密钥当做签名字符串，使用HmacSHA256算法计算签名，然后进行Base64 encode，最后再把签名参数再进行urlEncode，得到最终的签名（需要使用UTF-8字符集）。
	timeStampNow := time.Now().UnixNano() / 1000000
	signStr := fmt.Sprintf("%d\n%s", timeStampNow, Secret)

	hash := hmac.New(sha256.New, []byte(Secret))
	hash.Write([]byte(signStr))
	sum := hash.Sum(nil)

	encode := base64.StdEncoding.EncodeToString(sum)
	urlEncode := url.QueryEscape(encode)

	// 构建 请求 url
	defaultRobot = fmt.Sprintf("%s&timestamp=%d&sign=%s", defaultRobot, timeStampNow, urlEncode)

	// 构建 请求体
	request, _ := http.NewRequest("POST", defaultRobot, strings.NewReader(body))

	// 设置库端口
	client := &http.Client{}

	// 请求头添加内容
	request.Header.Set("Content-Type", "application/json")

	// 发送请求
	response, err := client.Do(request)
	if err != nil {
		fmt.Println(err.Error())
		return
	}
	fmt.Println("response: ", response)

	// 关闭 读取 reader
	defer response.Body.Close()

	// 读取内容
	all, _ := io.ReadAll(response.Body)
	fmt.Println("all: ", string(all))
}

func SendWithKeywords(notification model.Notification, defaultRobot string, env string, includeSeverity []string, atAll bool) {
	// 组装content
	content, _ := convertDingAlertsToText(notification, env, includeSeverity)
	if content == "" {
		fmt.Println("文本为空，不发送给钉钉")
		return
	}

	// 设置请求头
	requestBody := SimpleRequest{
		Text: SimpleRequestContent{
			Content: content,
		},
		At: SimpleRequestAt{
			AtMobiles: nil,
			AtUserIds: nil,
			IsAtAll:   atAll,
		},
		Msgtype: "text",
	}
	reqBodyBox, _ := json.Marshal(requestBody)
	body := string(reqBodyBox)

	// 构建 请求体
	request, _ := http.NewRequest("POST", defaultRobot, strings.NewReader(body))

	// 设置库端口
	client := &http.Client{}

	// 请求头添加内容
	request.Header.Set("Content-Type", "application/json")

	// 发送请求
	response, err := client.Do(request)
	if err != nil {
		fmt.Println(err.Error())
	}
	fmt.Printf("钉钉API 返回: %+v\n", response)

	// 关闭 读取 reader
	defer response.Body.Close()

	// 读取内容
	all, _ := io.ReadAll(response.Body)
	fmt.Println("钉钉API 返回body: ", string(all))
}
